-- limits.e think this was worked out on euforum in 2008?
--
-- Defines some of the numerical limits for Euphoria atoms and integers
--

--~ include machine.e
include std/convert.e



-- integer limits, 31 bits
global constant
    INTEGER_MIN = -(power(2, 30)),
    INTEGER_MAX = (-INTEGER_MIN) - 1

-- hex representation of double precision numerical limits
constant
    FLOAT64_MAX          = {#FF, #FF, #FF, #FF, #FF, #FF, #EF, #7F},
    FLOAT64_MIN          = {#00, #00, #00, #00, #00, #00, #10, #00}, -- corresponds to DBL_MIN in C
    FLOAT64_MIN_DENORMAL = {#01, #00, #00, #00, #00, #00, #00, #00}, -- absolute minimum
    FLOAT64_NAN_Q        = {#00, #00, #00, #00, #00, #00, #F8, #7F}, -- 0x7ff8000000 to 0x7fffffffff
    FLOAT64_NAN_S        = {#01, #00, #00, #00, #00, #00, #F0, #7F}, -- 0x7ff0000001 to 0x7ff7ffffff
    FLOAT64_INF          = {#00, #00, #00, #00, #00, #00, #F0, #7F}

-- atom limits, double precision
global constant
    ATOM_MAX = float64_to_atom(FLOAT64_MAX), -- 1.7976931348623160e+308
    ATOM_MIN = float64_to_atom(FLOAT64_MIN), -- 2.2250738585072020e-308
    ATOM_MIN_DENORMAL = float64_to_atom(FLOAT64_MIN_DENORMAL), -- 4.9406564584124650e-324
    ATOM_NAN_Q = float64_to_atom(FLOAT64_NAN_Q),
    ATOM_NAN_S = float64_to_atom(FLOAT64_NAN_S),
    ATOM_INFINITY = float64_to_atom(FLOAT64_INF)



-- We shouldn't compare directly to NAN or INF so use these functions to check.

global function is_infinite(atom a)
    sequence s
    integer isinf
    isinf = 0

    s = atom_to_float64(a)
    s[8] = and_bits(s[8], #7F) -- discard sign
    isinf = equal(s, FLOAT64_INF)
    
    return isinf

end function -- is_infinite()



global function is_nan(atom a)
    sequence s
    integer isnan
    isnan = 0

    s = atom_to_float64(a)
    s[8] = and_bits(s[8], #7F) -- discard sign

    if s[7] >= #F0 then
        if s[8] = #7F then
            if not equal(s, FLOAT64_INF) then isnan = 1
            end if
        else
            isnan = 0
        end if
    end if

    return isnan

end function -- is_nan()



function get_exponent(atom a)
    sequence s
    integer exponent

    s = atom_to_float64(a)
    -- mask and shift exponent bits (52..63), and subtract the exponent bias
    s[8] = and_bits(s[8], #7F) * 16
    s[7] = floor(s[7] / 16)
    exponent = or_bits(s[8], s[7]) - 1023

    return exponent

end function -- get_exponent()



-- epsilon is the smallest number that will cause a change in an atom value
-- should these three functions be in scientific.e instead?

global function get_epsilon(atom a)
    integer exp
    atom epsilon
    epsilon = 0.0

    exp = get_exponent(a)
    if exp > -1023 then
	    -- normalized, so implicit 1
        epsilon = power(2, exp - 52)
    else
        -- denormalized, so no implicit 1 
        epsilon = power(2, exp - 51)
    end if

    return epsilon

end function -- get_epsilon()



global function next_higher_atom( atom a )
	return a + get_epsilon( a )
end function -- next_higher_atom()



global function next_lower_atom( atom a )
	return a - get_epsilon( a )
end function -- next_lower_atom()

